﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace HML
{
    /// <summary>
    /// TabControlPlus下拉面板
    /// </summary>
    [Description("TabControlPlus下拉面板")]
    [TypeConverter(typeof(PropertyOrderConverter))]
    [ToolboxItem(false)]
    public class TabControlPlusPicker : DpiControl
    {
        #region 字段

        /// <summary>
        /// 列表区域Rect
        /// </summary>
        private Rectangle mainRect = Rectangle.Empty;
        /// <summary>
        /// 真实列表区域Rect
        /// </summary>
        private Rectangle mainRealityRect = Rectangle.Empty;

        /// <summary>
        /// 鼠标按下对象
        /// </summary>
        private object mousedownobject = null;
        /// <summary>
        /// 鼠标按下对象的坐标
        /// </summary>
        private Point mousedownpoint = Point.Empty;
        /// <summary>
        /// 鼠标进入对象
        /// </summary>
        private object mouseenterobject = null;

        /// <summary>
        /// 代表滚动条对象
        /// </summary>
        private readonly object scrollbarobject = new object();

        /// <summary>
        /// 所属控件
        /// </summary>
        private TabControlPlus ownerControl = null;

        #endregion

        public TabControlPlusPicker(TabControlPlus ownerControl)
        {
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            SetStyle(ControlStyles.Selectable, false);

            this.SuspendLayout();
            this.ownerControl = ownerControl;
            this.ResumeLayout();
        }

        #region 重写

        protected override void OnScaleDpiChangedInitialize()
        {
            this.InitializeElement();
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Graphics g = e.Graphics;
            int scale_itemHeight = (int)Math.Ceiling(this.ownerControl.Picker.TabItem.Height * this.ScaleDpi);
            LRPadding scale_itemLRPadding = new LRPadding((int)Math.Ceiling(this.ownerControl.Picker.TabItem.LRPadding.Right * this.ScaleDpi), (int)Math.Ceiling(this.ownerControl.Picker.TabItem.LRPadding.Right * this.ScaleDpi));
            int scale_itemIcoPadding = (int)(4 * this.ScaleDpi);
            int scale_trackThickness = (int)Math.Ceiling(this.ownerControl.Picker.ScrollBar.TrackThickness * this.ScaleDpi);

            //背景
            SolidBrush back_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.BackColor);
            g.FillRectangle(back_sb, new Rectangle(this.ClientRectangle.X, this.ClientRectangle.Y, (int)(this.ClientRectangle.Width - 1), this.ClientRectangle.Height - 1));
            back_sb.Dispose();

            // 选项
            SolidBrush item_normal_back_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.TabItem.Normal.BackColor);
            SolidBrush item_normal_text_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.TabItem.Normal.TextColor);
            SolidBrush item_enter_back_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.TabItem.Enter.BackColor);
            SolidBrush item_enter_text_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.TabItem.Enter.TextColor);
            SolidBrush item_disable_back_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.TabItem.Disabled.BackColor);
            SolidBrush item_disable_text_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.TabItem.Disabled.TextColor);
            Pen item_splitter_pen = null;
            LinearGradientBrush item_splitter_lgb = null;
            if (this.ownerControl.Picker.StylePattern == TabControlPlusPickerStylePattern.Line)
            {
                item_splitter_pen = new Pen(this.ownerControl.StyleAppearance.Picker.TabItem.SplitterColor, 1);
            }
            else if (this.ownerControl.Picker.StylePattern == TabControlPlusPickerStylePattern.GradualLine)
            {
                item_splitter_lgb = new LinearGradientBrush(new PointF(this.mainRect.X, this.mainRect.Y), new PointF(this.mainRect.Right, this.mainRect.Y), Color.Transparent, Color.Transparent);
                ColorBlend itemborder_cb = new ColorBlend();
                itemborder_cb.Colors = new Color[] { Color.Transparent, this.ownerControl.StyleAppearance.Picker.TabItem.SplitterColor, this.ownerControl.StyleAppearance.Picker.TabItem.SplitterColor, Color.Transparent };
                itemborder_cb.Positions = new float[] { 0.0f, 0.23f, 0.70f, 1.0f };
                item_splitter_lgb.InterpolationColors = itemborder_cb;
                item_splitter_pen = new Pen(item_splitter_lgb, 1);
            }

            int start_index = Math.Abs(this.mainRealityRect.Y) / scale_itemHeight;
            for (int i = start_index; i < this.ownerControl.TabPages.Count; i++)
            {
                TabPagePlus tab_page = this.ownerControl.TabPages[i];
                if (this.mainRealityRect.Y + tab_page.DropDownPanelItem_Rect.Y > this.mainRect.Bottom)
                {
                    break;
                }
                if (this.mainRealityRect.Y + tab_page.DropDownPanelItem_Rect.Bottom >= this.mainRect.Y)
                {
                    RectangleF item_rect = new RectangleF(tab_page.DropDownPanelItem_Rect.X, this.mainRealityRect.Y + tab_page.DropDownPanelItem_Rect.Y, tab_page.DropDownPanelItem_Rect.Width, tab_page.DropDownPanelItem_Rect.Height);

                    SolidBrush commom_back_sb = null;
                    SolidBrush commom_text_sb = null;
                    if (!tab_page.TabItem.Enabled)
                    {
                        commom_back_sb = item_disable_back_sb;
                        commom_text_sb = item_disable_text_sb;
                    }
                    else if (this.mouseenterobject == tab_page)
                    {
                        commom_back_sb = item_enter_back_sb;
                        commom_text_sb = item_enter_text_sb;
                    }
                    else
                    {
                        commom_back_sb = item_normal_back_sb;
                        commom_text_sb = item_normal_text_sb;
                    }

                    // 选项背景
                    g.FillRectangle(commom_back_sb, item_rect);

                    // 选项图标
                    if (this.ownerControl.Picker.TabItem.IconVisible)
                    {
                        bool ico_image_is_tmp = false;
                        Image ico_image = null;
                        this.ownerControl.GetProperty_ItemIcoImage(tab_page, ref ico_image, ref ico_image_is_tmp);

                        RectangleF ico_rect = new RectangleF(item_rect.X + scale_itemLRPadding.Left + scale_itemIcoPadding, item_rect.Y + scale_itemIcoPadding, item_rect.Height - scale_itemIcoPadding * 2, item_rect.Height - scale_itemIcoPadding * 2);
                        g.DrawImage(ico_image, ico_rect, new RectangleF(0, 0, ico_image.Width, ico_image.Height), GraphicsUnit.Pixel);

                        if (ico_image_is_tmp)
                        {
                            ico_image.Dispose();
                        }
                    }

                    // 选项文本
                    RectangleF text_rect = RectangleF.Empty;
                    if (this.ownerControl.Picker.TabItem.IconVisible)
                    {
                        text_rect = new RectangleF(item_rect.X + scale_itemLRPadding.Left + item_rect.Height, item_rect.Y + (item_rect.Height - tab_page.Picker_Text_Size.Height) / 2f, tab_page.Picker_Text_Size.Width, tab_page.Picker_Text_Size.Height);
                    }
                    else
                    {
                        text_rect = new RectangleF(item_rect.X + scale_itemLRPadding.Left, item_rect.Y + (item_rect.Height - tab_page.Picker_Text_Size.Height) / 2f, tab_page.Picker_Text_Size.Width, tab_page.Picker_Text_Size.Height);
                    }
                    g.DrawString(tab_page.Text, this.ownerControl.Picker.Font, commom_text_sb, text_rect);

                    // 选项分隔线
                    if (this.ownerControl.Picker.StylePattern != TabControlPlusPickerStylePattern.Default)
                    {
                        g.DrawLine(item_splitter_pen, item_rect.X, item_rect.Bottom - 1, item_rect.Right, item_rect.Bottom - 1);
                    }

                }
            }

            if (item_splitter_pen != null)
                item_splitter_pen.Dispose();
            if (item_splitter_lgb != null)
                item_splitter_lgb.Dispose();
            if (item_normal_back_sb != null)
                item_normal_back_sb.Dispose();
            if (item_enter_back_sb != null)
                item_enter_back_sb.Dispose();
            if (item_disable_back_sb != null)
                item_disable_back_sb.Dispose();
            if (item_normal_text_sb != null)
                item_normal_text_sb.Dispose();
            if (item_enter_text_sb != null)
                item_enter_text_sb.Dispose();
            if (item_disable_text_sb != null)
                item_disable_text_sb.Dispose();


            // 滚动条
            if (this.GetScrollBarVisableStatus())
            {
                // 轨道
                SolidBrush track_back_sb = new SolidBrush(this.ownerControl.StyleAppearance.Picker.ScrollBar.TrackBackColor);
                g.FillRectangle(track_back_sb, this.ownerControl.Picker.ScrollBar.Track_Rect);
                track_back_sb.Dispose();

                // 拇指
                PointF thumb_start = new PointF(this.ownerControl.Picker.ScrollBar.Thumb_Rect.X + scale_trackThickness / 2, this.ownerControl.Picker.ScrollBar.Thumb_Rect.Y);
                PointF thumb_end = new PointF(this.ownerControl.Picker.ScrollBar.Thumb_Rect.X + scale_trackThickness / 2, this.ownerControl.Picker.ScrollBar.Thumb_Rect.Bottom);
                Pen thumb_back_pen = new Pen(this.ownerControl.StyleAppearance.Picker.ScrollBar.ThumbBackColor, scale_trackThickness);
                g.DrawLine(thumb_back_pen, thumb_start, thumb_end);
                thumb_back_pen.Dispose();
            }

            // 边框
            Pen border_pen = new Pen(this.ownerControl.StyleAppearance.Picker.BorderColor);
            g.DrawRectangle(border_pen, new Rectangle(this.ClientRectangle.X, this.ClientRectangle.Y, (int)(this.ClientRectangle.Width - 1), this.ClientRectangle.Height - 1));
            border_pen.Dispose();

        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);

            if (this.DesignMode || this.ownerControl == null)
                return;

            Point point = this.PointToClient(Control.MousePosition);
            object mouseenterobject_tmp = null;

            if (this.GetScrollBarVisableStatus() && this.ownerControl.Picker.ScrollBar.Track_Rect.Contains(point))
            {
                mouseenterobject_tmp = this.ownerControl.Picker.ScrollBar.Thumb_Rect.Contains(point) ? this.scrollbarobject : null;
            }
            else if (this.mainRect.Contains(point))
            {
                TabPagePlus tab_page = this.FindMouseDownItem(point);
                if (tab_page != null && tab_page.TabItem.Enabled)
                {
                    mouseenterobject_tmp = tab_page;
                }
                else
                {
                    mouseenterobject_tmp = null;
                }
            }

            if (this.mouseenterobject != mouseenterobject_tmp)
            {
                this.mouseenterobject = mouseenterobject_tmp;
                this.Invalidate();
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);

            if (this.DesignMode || this.ownerControl == null)
                return;

            this.mousedownobject = null;
            this.mousedownpoint = Point.Empty;
            this.mouseenterobject = null;
            this.Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (this.DesignMode || this.ownerControl == null)
                return;

            if (e.Button == MouseButtons.Left)
            {
                this.mousedownobject = true;
                this.mousedownpoint = e.Location;

                if (this.GetScrollBarVisableStatus() && this.ownerControl.Picker.ScrollBar.Track_Rect.Contains(e.Location))
                {
                    this.mousedownobject = this.ownerControl.Picker.ScrollBar.Thumb_Rect.Contains(e.Location) ? this.scrollbarobject : null;
                    this.mousedownpoint = e.Location;
                }
                else if (this.mainRect.Contains(e.Location))
                {
                    TabPagePlus tab_page = this.FindMouseDownItem(e.Location);
                    if (tab_page != null && (tab_page.TabItem.Enabled || this.ownerControl.TabBar.TabItem.DisableActivation))
                    {
                        this.mousedownobject = tab_page;
                        this.mousedownpoint = Point.Empty;
                    }
                    else
                    {
                        this.mousedownobject = null;
                        this.mousedownpoint = Point.Empty;
                    }
                }
                else
                {
                    this.mousedownobject = null;
                    this.mousedownpoint = Point.Empty;
                }
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            if (this.DesignMode || this.ownerControl == null)
                return;

            if (e.Button == MouseButtons.Left)
            {
                if (this.mousedownobject != null && this.mousedownobject != this.scrollbarobject)
                {
                    TabPagePlus tab_page = this.FindMouseDownItem(e.Location);
                    int index = this.ownerControl.FindTabPage(tab_page);
                    if (tab_page != null && tab_page == this.mousedownobject)
                    {
                        this.ownerControl.SelectedIndex = index;
                        this.ownerControl.HidePicker();
                    }
                }
            }

            this.mousedownobject = null;
            this.mousedownpoint = Point.Empty;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (this.DesignMode || this.ownerControl == null)
                return;

            if (this.mousedownobject == null)
            {
                object mouseenterobject_tmp = null;
                if (this.GetScrollBarVisableStatus() && this.ownerControl.Picker.ScrollBar.Track_Rect.Contains(e.Location))
                {
                    mouseenterobject_tmp = this.ownerControl.Picker.ScrollBar.Thumb_Rect.Contains(e.Location) ? this.scrollbarobject : null;
                }
                else if (this.mainRect.Contains(e.Location))
                {
                    TabPagePlus tab_page = this.FindMouseDownItem(e.Location);
                    if (tab_page != null && tab_page.TabItem.Enabled)
                    {
                        mouseenterobject_tmp = tab_page;
                    }
                    else
                    {
                        mouseenterobject_tmp = null;
                    }
                }
                else
                {
                    mouseenterobject_tmp = null;
                }

                if (this.mouseenterobject != mouseenterobject_tmp)
                {
                    this.mouseenterobject = mouseenterobject_tmp;
                    this.Invalidate();
                }
            }
            else if (this.mousedownobject == this.scrollbarobject)
            {
                int offset = (int)((e.Location.Y - this.mousedownpoint.Y));
                this.mousedownpoint = e.Location;
                this.MouseMoveWheel(offset);
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            if (this.DesignMode || this.ownerControl == null)
                return;

            if (this.GetScrollBarVisableStatus())
            {
                int scale_itemHeight = (int)Math.Ceiling(this.ownerControl.Picker.TabItem.Height * this.ScaleDpi);

                int offset = e.Delta > 1 ? -1 : 1;
                offset = (int)Math.Ceiling(offset * scale_itemHeight / 2f);
                this.MouseMoveWheel(offset);
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            this.InitializeElement();
            this.Invalidate();
        }

        #endregion

        #region 公开方法

        /// <summary>
        /// 初始化控件内部元素布局
        /// </summary>
        public void InitializeElement()
        {
            this.InitializeMainRectangle();
            this.InitializeMainRealityRectangle();
            this.InitializeScrollBarRectangle();
            this.UpdateItemsRect();
        }

        #endregion 

        #region 私有方法

        /// <summary>
        /// 初始化列表区域Rect
        /// </summary>
        private void InitializeMainRectangle()
        {
            this.mainRect = new Rectangle(this.ClientRectangle.X, this.ClientRectangle.Top, this.ClientRectangle.Width, this.ClientRectangle.Height);
        }

        /// <summary>
        /// 初始化真实列表区域Rect
        /// </summary>
        private void InitializeMainRealityRectangle()
        {
            int scale_itemHeight = (int)Math.Ceiling(this.ownerControl.Picker.TabItem.Height * this.ScaleDpi);

            this.mainRealityRect = new Rectangle(this.mainRect.X, this.mainRect.Y, this.mainRect.Width, 0);

            int y = this.mainRealityRect.Y;
            if (this.mainRealityRect.Bottom < this.mainRect.Bottom)
            {
                y += (this.mainRect.Bottom - this.mainRealityRect.Bottom);
            }
            if (y > this.mainRect.Y)
            {
                y = this.mainRect.Y;
            }
            this.mainRealityRect = new Rectangle(this.mainRealityRect.X, y, this.mainRealityRect.Width, this.mainRealityRect.Height);

            int h = 0;
            for (int i = 0; i < this.ownerControl.TabPages.Count; i++)
            {
                h += scale_itemHeight;
                this.ownerControl.TabPages[i].DropDownPanelItem_Rect = new RectangleF(this.mainRect.Left, this.mainRect.Top + i * scale_itemHeight, this.mainRect.Width, scale_itemHeight);
            }
            this.mainRealityRect = new Rectangle(this.mainRealityRect.X, this.mainRealityRect.Y, this.mainRealityRect.Width, h);

            this.UpdateScrollBarThumbRectLocation();
        }

        /// <summary>
        /// 初始化滚动条Rect
        /// </summary>
        private void InitializeScrollBarRectangle()
        {
            int scale_trackThickness = (int)Math.Ceiling(this.ownerControl.Picker.ScrollBar.TrackThickness * this.ScaleDpi);

            this.ownerControl.Picker.ScrollBar.Track_Rect = new Rectangle((int)this.mainRect.Right - scale_trackThickness, this.mainRect.Top, scale_trackThickness, this.mainRect.Height);
            float proportion = (float)this.mainRect.Height / (float)this.mainRealityRect.Height;
            if (proportion > 1)
            {
                proportion = 1;
            }
            float thumb_height = this.ownerControl.Picker.ScrollBar.Track_Rect.Height * proportion;
            if (thumb_height < this.ownerControl.Picker.ScrollBar.ThumbMinSize)
            {
                thumb_height = this.ownerControl.Picker.ScrollBar.ThumbMinSize;
            }
            this.ownerControl.Picker.ScrollBar.Thumb_Rect = new RectangleF(this.ownerControl.Picker.ScrollBar.Track_Rect.X, this.ownerControl.Picker.ScrollBar.Track_Rect.Y, scale_trackThickness, thumb_height);
        }

        /// <summary>
        /// 初始化控件状态
        /// </summary>
        internal void InitializeStatus()
        {
            this.mousedownobject = null;
            this.mousedownpoint = Point.Empty;
            this.mouseenterobject = null;
            this.Invalidate();
        }

        /// <summary>
        /// 更新拇指的RectLocation
        /// </summary>
        private void UpdateScrollBarThumbRectLocation()
        {
            float thumb_height = this.ownerControl.Picker.ScrollBar.Track_Rect.Height * ((float)this.mainRect.Height / ((float)this.mainRealityRect.Height));
            if (thumb_height < this.ownerControl.Picker.ScrollBar.ThumbMinSize)
            {
                thumb_height = this.ownerControl.Picker.ScrollBar.ThumbMinSize;
            }
            float h = this.mainRect.Y - this.mainRealityRect.Y;
            if (this.mainRealityRect.Y < 0)
            {
                h = this.mainRect.Y + Math.Abs(this.mainRealityRect.Y);
            }
            float slide_y = this.ownerControl.Picker.ScrollBar.Track_Rect.Y + (this.ownerControl.Picker.ScrollBar.Track_Rect.Height - thumb_height) * h / (float)(this.mainRealityRect.Height - this.mainRect.Height);

            this.ownerControl.Picker.ScrollBar.Thumb_Rect = new RectangleF(this.ownerControl.Picker.ScrollBar.Track_Rect.X, slide_y, this.ownerControl.Picker.ScrollBar.Thumb_Rect.Width, thumb_height);

        }

        /// <summary>
        /// 获取滚动条当前显示状态
        /// </summary>
        /// <returns></returns>
        private bool GetScrollBarVisableStatus()
        {
            return (this.mainRealityRect.Height > this.mainRect.Height);
        }

        /// <summary>
        /// 更新所有选项Rect
        /// </summary>
        private void UpdateItemsRect()
        {
            int scale_itemHeight = (int)Math.Ceiling(this.ownerControl.Picker.TabItem.Height * this.ScaleDpi);

            for (int i = 0; i < this.ownerControl.TabPages.Count; i++)
            {
                this.ownerControl.TabPages[i].DropDownPanelItem_Rect = new RectangleF(this.mainRect.Left, this.mainRealityRect.Top + i * scale_itemHeight, this.mainRect.Width, scale_itemHeight);
            }
        }

        /// <summary>
        /// 根据坐标查找鼠标所在的选项
        /// </summary>
        /// <param name="mousePoint">坐标</param>
        /// <returns>没有为null</returns>
        private TabPagePlus FindMouseDownItem(Point mousePoint)
        {
            if (!this.mainRect.Contains(mousePoint))
            {
                return null;
            }

            int scale_itemHeight = (int)Math.Ceiling(this.ownerControl.Picker.TabItem.Height * this.ScaleDpi);
            int y = Math.Abs(this.mainRealityRect.Y) + mousePoint.Y;
            int index = y / scale_itemHeight;

            return this.ownerControl.GetTabPage(index);
        }

        /// <summary>
        /// 滚动条移动或鼠标滚轮移动
        /// </summary>
        /// <param name="offset"></param>
        private void MouseMoveWheel(int offset)
        {
            float y = this.ownerControl.Picker.ScrollBar.Thumb_Rect.Y;
            y += offset;
            if (y < this.ownerControl.Picker.ScrollBar.Track_Rect.Y)
            {
                y = this.ownerControl.Picker.ScrollBar.Track_Rect.Y;
            }
            if (y > this.ownerControl.Picker.ScrollBar.Track_Rect.Bottom - this.ownerControl.Picker.ScrollBar.Thumb_Rect.Height)
            {
                y = this.ownerControl.Picker.ScrollBar.Track_Rect.Bottom - this.ownerControl.Picker.ScrollBar.Thumb_Rect.Height;
            }

            this.ownerControl.Picker.ScrollBar.Thumb_Rect = new RectangleF(this.ownerControl.Picker.ScrollBar.Thumb_Rect.Location.X, y, this.ownerControl.Picker.ScrollBar.Thumb_Rect.Width, this.ownerControl.Picker.ScrollBar.Thumb_Rect.Height);

            float proportion = (float)(this.ownerControl.Picker.ScrollBar.Thumb_Rect.Y - this.ownerControl.Picker.ScrollBar.Track_Rect.Y) / (float)(this.ownerControl.Picker.ScrollBar.Track_Rect.Height - this.ownerControl.Picker.ScrollBar.Thumb_Rect.Height);

            float scroll_height = this.mainRealityRect.Height - this.mainRect.Height < 0 ? 0 : (this.mainRealityRect.Height - this.mainRect.Height);
            this.mainRealityRect.Y = (int)(this.mainRect.Y - scroll_height * proportion);

            this.Invalidate();
        }

        #endregion

    }
}
